<?php
include_once('Document.php.inc');
include_once('main.php');

class Collection {
	public $docs = array(); #Documents in collection
	public $docsById=array();
	private $noOfDocs; #Total no of documents in collection
	public $termDocFreq = array(); #No of documents with term
	private $dimensionsLSI; #no of dimensions to use in LSI
	private $Sinv = array(); #LSI
	private $Ut = array();   #LSI
	private $wLSI = array(); #LSI
	private $mapDocs = array(); #Map documents to integers
	private $mapDocsInv = array(); #Map integers to documents
	public $mapTerms = array(); #Map terms to integers
	public $mapTermsInv = array(); #Map integers to terms
	public $idfFactors = array(); #IDF-factor requested in lab
	private $util; # = new IRutils;

	public function Collection() {
		$this->util = new IRutils;
	}



	function owerwriteWeights($m){
		$rows = count($m);
		$cols = count($m[0]);
		$tmpweights=array();
		for($j=0; $j < $cols; $j++) {
			$tmpweights[$j]=array();
			for($i=0; $i < $rows; $i++) {
				$tmpweights[$j][]=$m[$i][$j];
			}
		}
		for($j=0; $j < $cols; $j++) {
			$docid=$this->mapDocsInv[$j];
			$doc= $this->docsById[$docid];
			$doc->weights=$tmpweights[$j];
			//var_dump($tmpweights[$j]);
		}

	}

	function getMaxWeight(){
		$maxWeight = 0;
		foreach($this->docs as $doc){
			foreach($doc->weights as $term => $weight){
				$tempMax = max($doc->weights);
				if($tempMax > $maxWeight){
					$maxWeight = $tempMax;
				}
			}
		}
		return $maxWeight;
	}

	function addDoc($id, $title, $text, $label) {
		$d = new Document;
		$d->id = $id;
		$d->title = $title;
		if($label!=-2){
			$d->importLabeledWords($text, $label);
		}else{
			$d->importWords($text);
		}

		$this->docs[] = $d;
		$this->docsById[$id]=$d;
		$this->noOfDocs++;
		return $d;
	}

	function changeLabels($classifications){
		for($i = 0; $i < $this->noOfDocs; $i++){
			$this->docs[$i]->setLabel($classifications[$i]);
		}
	}

	function mergeCollectionDocs($otherColl){
		$this->noOfDocs +=  $otherColl->noOfDocs;
		foreach($otherColl->docs as $doc){
			$this->docs[] = $doc;
		}
		return $this;
	}

	function normalizeWeights($maxWeight){
		foreach($this->docs as $doc){
			foreach($doc->weights as $term => $weight){
				$doc->weights[$term] = (($weight/$maxWeight) * 2) - 1;
			}
		}

	}

	function printToLibSVMFormat($filePath){
		$data = "";
		foreach($this->docs as $doc){
			$data .= $doc->getLabel();
			foreach($this->mapTermsInv as $index => $term){
				$value = 0;
				if(isset($doc->weights[$term])){ //<=> to == null?
					$value = $doc->weights[$term];
				}
				$data .= " ".($index+1).":".$value;
			}
			$data .= "\r\n";
		}
		//echo $data."\r\n";
		file_put_contents($filePath, $data);
	}

	function calculateLab1Models($queryCollection){
		$searchResultsBOOL = array(); #key: query, value: assosiative array ( with key: docid(from documentcollection), value: matching value for the query)
		$searchResultsVSM_BOOL = array();
		$searchResultsVSM = array();
		foreach($queryCollection->docs as $queryAsDocument){
			$query_string= $queryAsDocument->id;
			$searchResultsBOOL[$query_string]=$this->calculateBooleanModel($queryAsDocument);
			$searchResultsVSM_BOOL[$query_string]=$this->calculateVSM_bool($queryAsDocument);
			$searchResultsVSM[$query_string]=$this->calculateVSM($queryAsDocument);
		}
		$results=array(); # assosiative array, store the results by model(key) the value is another assosiative array containing the resuls(aaaosiative arry) for a specific query(key)
		$results[Models::BOOL]=$searchResultsBOOL;
		$results[Models::VSM_BOOL]=$searchResultsVSM_BOOL;
		$results[Models::VSM]=$searchResultsVSM;
		return $results;
	}

	private function calculateBooleanModel($queryAsDocument){
		$nbrOfQueryWords = count($queryAsDocument->termFreq); // antalet ord i denna query
		foreach($this->docs as $index => $doc){ // för alla document i dokumentsamlingen
			$queryWordsExistingInDocument = 0; //om ett ord från query finns i detta dokument så ++ på denna
			foreach($queryAsDocument->termFreq as $term => $freq){ // iterera över alla ord i query
				if(isset($doc->termFreq[$term])){ // om ordet i auery även finns i dokumentet som kontrolleras
					$queryWordsExistingInDocument++; // ordet finns i dokumentet...
				}
			}
			if($queryWordsExistingInDocument == $nbrOfQueryWords){ //om alla ord i queryn finns i dokumentet så är resultatet ett
				$results[$doc->id] = 1;
			} else{ // annars är resultatet 0
				$results[$doc->id] = 0;
			}
		}
		return $results; // resultatet för alla dokument, key: dokument id, Value: 1 om alla ord fanns med annars 0
	}

	private function calculateVSM_bool($query_string){
		$results = array();
		foreach($this->docs as $index => $doc){
			$dotprod=0;
			foreach($query_string->termFreq as $term => $freq){
				#echo "Term: ".$term." freq: ".$freq;
				if(isset($doc->termFreq[$term])){
					$dotprod++;
				}
			}
			$divisor=($this->norm_bool($doc->termFreq)*$this->norm_bool($query_string->termFreq));
			$cos_angle;
			if($divisor!=0){
				$cos_angle=$dotprod/$divisor;
			}else{
				$cos_angle=0;
			}
			$results[$doc->id]=$cos_angle;

		}
		return $results;
	}
	public function calculateVSM_PAGERANK($pageranks, $vsm_result){
		$result=array();
		foreach($vsm_result as $query => $array){
			foreach ($array as $docId => $vsmValue){
				if(isset($pageranks[$docId])){
					//	echo("Using pagerank\r\n");
					$result[$query][$docId]= 1000 * $pageranks[$docId] * $vsmValue;
				}else{
					$result[$query][$docId]= 1000 * min($pageranks)/2 * $vsmValue;
				}
			}
		}
		return $result;
	}
	private function calculateVSM($query){
		global $useSVD;

			

		foreach($this->docs as $index => $doc){
			$dotprod=0;


			/*foreach($query->weights as $term => $weight){
				if(isset($doc->weights[$term])){
				$dotprod += $doc->weights[$term] * $query->weights[$term]; #Om en term i query också finns i ett dokument som genomsöks så läggs termens weight från dokumentet in i dotprod
				}
				}*/

			if(!$useSVD){
				foreach($query->termFreq as $term => $freq){
					#echo "Term: ".$term." freq: ".$freq;
					if(isset($doc->termFreq[$term])){
						$dotprod += $doc->weights[$term] * $query->weights[$term]; #Om en term i query också finns i ett dokument som genomsöks så läggs termens weight från dokumentet in i dotprod
					}
				}
			} else{
				$nbrConcepts = count($query->weights);
				//var_dump($query->weights);
				for($i = 0; $i < $nbrConcepts; $i++){
					if(isset($doc->weights[$i])){
						$dotprod += $doc->weights[$i] * $query->weights[$i]; #Om en term i query också finns i ett dokument som genomsöks så läggs termens weight från dokumentet in i dotprod
					}	
				}
				//echo $dotprod . "\n";
			}


			$divisor=($this->norm($doc->weights)*$this->norm($query->weights));
			$cos_angle;
			if($divisor!=0){
				$cos_angle=$dotprod/$divisor;
			}else{
				$cos_angle=0;
			}
			$results[$doc->id]=$cos_angle;
			//echo ("SVM: ".$doc->id.": ".$cos_angle."\r\n");
		}
		return $results;
	}
	#Normera en vektor
	function norm($vector){
		$sum=0;
		foreach($vector as $term =>$freq){
			$sum += $freq*$freq;
		}
		return sqrt($sum);
	}
	#Normera en vektor men använd endast 1 som värde för alla värden >0
	function norm_bool($vector){
		$sum = 0;
		foreach($vector as $term =>$freq){
			if($freq > 0){
				$sum++;
			}
		}
		return sqrt($sum);
	}

	function calcTermDocFreq() { # built in lab 1
		for ($i=0; $i < $this->noOfDocs; $i++) {
			$doc = ($this->docs[$i]);
			foreach ($doc->termFreq as $term => $tFreq){
				if(!isset($this->termDocFreq[$term])){
					$this->termDocFreq[$term] = 1;
				} else {
					$this->termDocFreq[$term]++;
				}
			}
		}
	}
	function calcIdfFactor() { #requires calcTermDocFreq to be executed before this
		foreach ($this->termDocFreq as $term => $docuTermFrequency) {
			$this->idfFactors[$term] = log (($this->noOfDocs)/($docuTermFrequency), 2);
		}
	}

	function calcTfIdf(){
		//echo("nbr of docs was ".$this->noOfDocs."\r\n");
		for ($i=0; $i < $this->noOfDocs; $i++) {
			$doc = ($this->docs[$i]);
			foreach ($doc->termFreq as $term => $tFreq){
				if(isset($this->idfFactors[$term])){ //om det finns en idf factor
					$newWeight= $tFreq * ($this->idfFactors[$term]);
					//	echo("idffactor for term: ".$term." was ".$this->idfFactors[$term]);
				}else{// annars sätt idffactor till något högt????
					$newWeight= $tFreq * 0; // this does not matter at all
					//echo("TEEEEEERM: ".$tFreq);
				}
					
				$doc->weights[$term] = $newWeight;
			}
		}
	}

	function printTfIdf(){
		print "\n";
		printf("%12s", "tf-idf factor");
		print "\n";
		for ($i=0; $i < $this->noOfDocs; $i++) {
			$doc = ($this->docs[$i]);
			printf("%4s", "DocId");
			printf("%6s",substr($doc->id,0,5));
			print "\n";
			foreach ($doc->weights as $term => $weight){
				printf("%30s", $term);
				printf("%12f",$weight);
				print "\n";
			}
		}
	}

	function printIdfFactor(){ # built in lab 1
		print "\n";
		printf("%12s", "idf factor");
		print "\n";
		foreach ($this->idfFactors as $term => $idf) {
			printf("%12s", $term);
			printf("%12f",$idf);
			print "\n";
		}
	}

	function printFreqMatrix () {
		printf("%12s", "DocId");
		foreach ($this->docs as $d) {
			printf("%6s",substr($d->id,0,5));
		}
		print " DocFreq";
		print "\n";
		foreach ($this->termDocFreq as $word => $docfreq) {
			printf("%12s", $word);
			foreach ($this->docs as $d) {
				if(isset($d->termFreq[$word])){
					printf("%6d",$d->termFreq[$word]);
				}
				else{
					printf("%6d", 0);
				}
			}
			printf("%6d",$docfreq);
			print "\n";
		}
	}

	function printIndexedDocs(){
		printf("%12s", "Indexed documents: ");
		print "\n";
		foreach ($this->docs as $d) {
			printf("%6s",$d->id);
			print "\n";
			printf("%6s",$d->title);
			print "\n";
			print "\n";
		}
	}


	## SVD stuff ##
	function mapDocsTerms2numbers() {
		#Map documentIds and terms to numbers and vice versa
		#Required by the saveWeightMatrixCompressed() function
		$n=0;
		foreach ($this->docs as $d) {
			$this->mapDocs[$d->id]=$n;
			$this->mapDocsInv[$n]=$d->id;
			$n++;
		}

		$n=0;
		foreach ($this->termDocFreq as $word => $docfreq) {
			$this->mapTerms[$word]=$n;
			$this->mapTermsInv[$n]=$word;
			$n++;
		}
	}

	function saveWeightMatrixCompressed() { #for SVD
		echo("SPARAR\r\n");
		#You must call mapDocsTerms2numbers() before using this function
		$wm = array();
		$colNonZero = array();
		$t=count($this->mapTerms); #terms=rows
		echo("Rows was: ".$t."\r\n");
		$N=count($this->mapDocs); #docs=cols
		echo("Cols was: ".$N."\r\n");
		$antNonZero=0;
		#document-term weight matrix $t x $N
		foreach ($this->mapTerms as $word => $i) {   #terms=rows //Iterate all terms
			foreach ($this->docs as $d) { #docs=cols //iterate all docs
				$j=$this->mapDocs[$d->id];
				if(isset($d->weights[$word])){
					$val=$d->weights[$word];
					$wm[$i][$j]=$val;
				}else{
					$wm[$i][$j]=0;
				}
				if ($wm[$i][$j]>0) {
					$antNonZero++;
					if(!isset($colNonZero[$j])){
						$colNonZero[$j]=1;
					}else{
						$colNonZero[$j]++;
					}
				}
			}
		}
		$res = "$t $N $antNonZero\n";
		foreach ($this->mapDocs as $recid => $j) { //iterate
			$n=$colNonZero[$j];

			$res .= "$n\n";
			foreach ($this->mapTerms as $word => $i) {
				//	if(isset($this->termDocFreq[$word])){
				$val = $wm[$i][$j];
				if ($val>0) {
					$res .= "$i $val\n";
				}
				//}
			}

		}
		file_put_contents('./svddata/termDocMatrix.txt', $res);

	}

}

/*
 function saveWeightMatrixCompressed() { #for SVD
 echo("SPARAR\r\n");
 #You must call mapDocsTerms2numbers() before using this function
 $wm = array();
 $colNonZero = array();
 $t=count($this->mapTerms); #terms=rows
 echo("Rows was: ".$t."\r\n");
 $N=count($this->mapDocs); #docs=cols
 echo("Cols was: ".$N."\r\n");
 $antNonZero=0;
 #document-term weight matrix $t x $N
 foreach ($this->mapTerms as $word => $i) {   #terms=rows //Iterate all terms
 foreach ($this->docs as $d) { #docs=cols //iterate all docs
 $j=$this->mapDocs[$d->id];
 if(isset($d->weights[$word])){
 $val=$d->weights[$word];
 $wm[$i][$j]=$val;
 }else{
 $wm[$i][$j]=0;
 }
 if ($wm[$i][$j]>0) {
 $antNonZero++;
 if(!isset($colNonZero[$j])){
 $colNonZero[$j]=1;
 }else{
 $colNonZero[$j]++;
 }
 }
 }
 }
 $res = "$t $N $antNonZero\n";
 foreach ($this->mapDocs as $recid => $j) { //iterate
 $n=$colNonZero[$j];

 $res .= "$n\n";
 foreach ($this->mapTerms as $word => $i) {
 //	if(isset($this->termDocFreq[$word])){
 $val = $wm[$i][$j];
 if ($val>0) {
 $res .= "$i $val\n";
 }
 //}
 }

 }
 file_put_contents('./svddata/termDocMatrix.txt', $res);

 }
 */
?>